package com.gitee.magic.context;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Timestamp;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.gitee.magic.context.converter.ArraysConverterEditor;
import com.gitee.magic.converter.BigDecimalArrayConverterEditor;
import com.gitee.magic.converter.BigDecimalConverterEditor;
import com.gitee.magic.converter.BigIntegerArrayConverterEditor;
import com.gitee.magic.converter.BigIntegerConverterEditor;
import com.gitee.magic.converter.BooleanArrayConverterEditor;
import com.gitee.magic.converter.BooleanBigArrayConverterEditor;
import com.gitee.magic.converter.BooleanConverterEditor;
import com.gitee.magic.converter.ByteArrayConverterEditor;
import com.gitee.magic.converter.ByteConverterEditor;
import com.gitee.magic.converter.CharArrayConverterEditor;
import com.gitee.magic.converter.CharacterConverterEditor;
import com.gitee.magic.converter.DateConverterEditor;
import com.gitee.magic.converter.DoubleArrayConverterEditor;
import com.gitee.magic.converter.DoubleBigArrayConverterEditor;
import com.gitee.magic.converter.DoubleConverterEditor;
import com.gitee.magic.converter.EnumConverterEditor;
import com.gitee.magic.converter.FloatArrayConverterEditor;
import com.gitee.magic.converter.FloatBigArrayConverterEditor;
import com.gitee.magic.converter.FloatConverterEditor;
import com.gitee.magic.converter.IntBigArrayConverterEditor;
import com.gitee.magic.converter.IntegerArrayConverterEditor;
import com.gitee.magic.converter.IntegerConverterEditor;
import com.gitee.magic.converter.JsonArrayConverterEditor;
import com.gitee.magic.converter.JsonObjectConverterEditor;
import com.gitee.magic.converter.LocalDateConverterEditor;
import com.gitee.magic.converter.LocalDateTimeConverterEditor;
import com.gitee.magic.converter.LocalTimeConverterEditor;
import com.gitee.magic.converter.LongArrayConverterEditor;
import com.gitee.magic.converter.LongBigArrayConverterEditor;
import com.gitee.magic.converter.LongConverterEditor;
import com.gitee.magic.converter.PropertiesConverterEditor;
import com.gitee.magic.converter.ShortArrayConverterEditor;
import com.gitee.magic.converter.ShortBigArrayConverterEditor;
import com.gitee.magic.converter.ShortConverterEditor;
import com.gitee.magic.converter.SqlDateConverterEditor;
import com.gitee.magic.converter.SqlTimeConverterEditor;
import com.gitee.magic.converter.SqlTimestampConverterEditor;
import com.gitee.magic.converter.StringArrayConverterEditor;
import com.gitee.magic.converter.StringBufferConverterEditor;
import com.gitee.magic.converter.StringBuilderConverterEditor;
import com.gitee.magic.converter.StringConverterEditor;
import com.gitee.magic.core.converter.AbstractConverterEditor;
import com.gitee.magic.core.exception.ApplicationException;
import com.gitee.magic.core.json.JsonArray;
import com.gitee.magic.core.json.JsonObject;

/**
 * @author start
 */
public class PropertyConverterEditor {

	protected static final Logger LOGGER = LoggerFactory.getLogger(PropertyConverterEditor.class);

	private static Map<Class<?>,Class<? extends AbstractConverterEditor<?>>> basicConverter=new ConcurrentHashMap<>();
	private static Map<Class<?>,Map<String,Class<? extends AbstractConverterEditor<?>>>> customConverter=new ConcurrentHashMap<>();
	
	static {
		//内部类型
		basicConverter.put(byte.class, ByteConverterEditor.class);
		basicConverter.put(char.class, CharacterConverterEditor.class);
		basicConverter.put(boolean.class, BooleanConverterEditor.class);
		basicConverter.put(short.class, ShortConverterEditor.class);
		basicConverter.put(int.class, IntegerConverterEditor.class);
		basicConverter.put(long.class, LongConverterEditor.class);
		basicConverter.put(float.class,FloatConverterEditor.class);
		basicConverter.put(double.class, DoubleConverterEditor.class);
		basicConverter.put(Byte.class, ByteConverterEditor.class);
		basicConverter.put(Character.class, CharacterConverterEditor.class);
		basicConverter.put(Boolean.class, BooleanConverterEditor.class);
		basicConverter.put(Short.class, ShortConverterEditor.class);
		basicConverter.put(Integer.class, IntegerConverterEditor.class);
		basicConverter.put(Long.class, LongConverterEditor.class);
		basicConverter.put(Float.class, FloatConverterEditor.class);
		basicConverter.put(Double.class, DoubleConverterEditor.class);
		basicConverter.put(BigInteger.class, BigIntegerConverterEditor.class);
		basicConverter.put(BigDecimal.class, BigDecimalConverterEditor.class);
		basicConverter.put(String.class,StringConverterEditor.class);
		basicConverter.put(StringBuffer.class, StringBufferConverterEditor.class);
		basicConverter.put(StringBuilder.class, StringBuilderConverterEditor.class);
		basicConverter.put(byte[].class, ByteArrayConverterEditor.class);
		basicConverter.put(char[].class, CharArrayConverterEditor.class);
		basicConverter.put(boolean[].class, BooleanBigArrayConverterEditor.class);
		basicConverter.put(short[].class, ShortBigArrayConverterEditor.class);
		basicConverter.put(int[].class, IntBigArrayConverterEditor.class);
		basicConverter.put(long[].class, LongBigArrayConverterEditor.class);
		basicConverter.put(float[].class,FloatBigArrayConverterEditor.class);
		basicConverter.put(double[].class, DoubleBigArrayConverterEditor.class);
		basicConverter.put(Byte[].class, ByteArrayConverterEditor.class);
		basicConverter.put(Character[].class, CharArrayConverterEditor.class);
		basicConverter.put(Boolean[].class, BooleanArrayConverterEditor.class);
		basicConverter.put(Short[].class, ShortArrayConverterEditor.class);
		basicConverter.put(Integer[].class, IntegerArrayConverterEditor.class);
		basicConverter.put(Long[].class, LongArrayConverterEditor.class);
		basicConverter.put(Float[].class,FloatArrayConverterEditor.class);
		basicConverter.put(Double[].class, DoubleArrayConverterEditor.class);
		basicConverter.put(BigInteger[].class, BigIntegerArrayConverterEditor.class);
		basicConverter.put(BigDecimal[].class, BigDecimalArrayConverterEditor.class);
		basicConverter.put(String[].class,StringArrayConverterEditor.class);
		basicConverter.put(JsonObject.class,JsonObjectConverterEditor.class);
		basicConverter.put(JsonArray.class,JsonArrayConverterEditor.class);
		basicConverter.put(Timestamp.class,SqlTimestampConverterEditor.class);
		basicConverter.put(java.sql.Time.class,SqlTimeConverterEditor.class);
		basicConverter.put(java.sql.Date.class,SqlDateConverterEditor.class);
		basicConverter.put(java.util.Date.class,DateConverterEditor.class);
		basicConverter.put(LocalTime.class,LocalTimeConverterEditor.class);
		basicConverter.put(LocalDate.class,LocalDateConverterEditor.class);
		basicConverter.put(LocalDateTime.class,LocalDateTimeConverterEditor.class);
		basicConverter.put(Properties.class,PropertiesConverterEditor.class);
	}
	
	/**
	 * 是否支持转写
	 * @param classes
	 * @return
	 */
	public static boolean isSupportConverter(Class<?> classes) {
		return getConverterClasses(classes)!=null;
	}
	
	public static Class<? extends AbstractConverterEditor<?>> getConverterClasses(Class<?> classes){
		Class<? extends AbstractConverterEditor<?>> prototype=basicConverter.get(classes);
		if(prototype!=null) {
			return prototype;
		}else if(classes.isEnum()) {
			return EnumConverterEditor.class;
		}else if(classes.isArray()) {
			return ArraysConverterEditor.class;
//		}else if(List.class.isAssignableFrom(classes)) {
//			return ListConverterEditor.class;
//		}else if(Map.class.isAssignableFrom(classes)) {
//			return MapConverterEditor.class;
//		}else {
//			return ObjectConverterEditor.class;
		}
		return null;
	}

	/**
	 * 获取内部转写类型
	 * @param classes
	 * @return
	 */
	public static AbstractConverterEditor<?> getBasicConverter(Class<?> classes) {
//		Class<?> prototype=getConverterClasses(classes);
//		if(prototype!=null) {
//			return newInstance(prototype,classes);
//		}
		return getBasicConverter(classes,classes);
	}
	
	/**
	 * 获取内部转写类型
	 * @param target 目标类型
	 * @param classes 真实类型
	 * @return
	 */
	public static AbstractConverterEditor<?> getBasicConverter(Class<?> target,Type classes) {
		Class<?> prototype=getConverterClasses(target);
		if(prototype!=null) {
			return newInstance(prototype,classes);
		}
		return null;
	}
	
	public static AbstractConverterEditor<?> newInstance(Class<?> target,Type classes){
		Constructor<?> ct=null;
		try {
			ct=target.getDeclaredConstructor(new Class[]{Class.class});
		} catch (NoSuchMethodException | SecurityException e) {
			throw new ApplicationException(e);
		}
		try {
			return (AbstractConverterEditor<?>)ct.newInstance(classes);
		} catch (InstantiationException | IllegalAccessException | IllegalArgumentException
				| InvocationTargetException e) {
			throw new ApplicationException(e);
		}
	}
	
	/**
	 * 获取自定义转写类型
	 */
	private static AbstractConverterEditor<?> getCustomConverter(Class<?> classes,Class<?> type,String fieldName) {
		Map<String,Class<? extends AbstractConverterEditor<?>>> prototype=customConverter.get(classes);
		if(prototype==null) {
			return null;
		}
		Class<? extends AbstractConverterEditor<?>> editor=prototype.get(fieldName);
		if(editor==null) {
			return null;
		}
		return newInstance(editor,type);
	}
	
	private static AbstractConverterEditor<?> getConverter(Class<?> classes,Class<?> type,String fieldName) {
		//先获取自定义再获取内部转换器
		AbstractConverterEditor<?> editor=getCustomConverter(classes,type, fieldName);
		if(editor==null) {
			editor=(AbstractConverterEditor<?>)getBasicConverter(type);
		}
		if(editor==null) {
//			throw new NullPointerException("类<"+classes+">，字段："+fieldName+"找不到对应的转写类型");
		}
		return editor;
	}

	/**
	 * 设置当前自定义类型
	 * @param classes
	 * @return
	 */
	public static void putBasicConverterType(Class<?> classes,Class<? extends AbstractConverterEditor<?>> editor) {
		basicConverter.put(classes, editor);
	}

	/**
	 * 设置当前自定义类型
	 * @param classes
	 * @return
	 */
	public static void putCustomConverterType(Class<?> classes,String fieldName,Class<? extends AbstractConverterEditor<?>> editor) {
		Map<String,Class<? extends AbstractConverterEditor<?>>> editors=customConverter.get(classes);
		if(editors==null) {
			editors=new HashMap<>(100);
		}
		editors.put(fieldName, editor);
		customConverter.put(classes, editors);
	}

	/**
	 * 从外部读取进入应用
	 * @param type
	 * @param value
	 * @return
	 */
	public static Object in(Class<?> prototype,String fieldName,Class<?> type,Object value) {
		if(LOGGER.isDebugEnabled()) {
			LOGGER.debug("in:{}:{}\t{}",prototype,fieldName,value);
		}
		AbstractConverterEditor<?> editor=getConverter(prototype,type,fieldName);
		if(editor==null) {
			return value;
		}
		editor.restore(value);
		return editor.getValue();
	}
	
	/**
	 * 从应用转换输出到外部
	 * @param type
	 * @param value
	 * @return
	 */
	public static Object out(Class<?> prototype,String fieldName,Class<?> type,Object value) {
		if(LOGGER.isDebugEnabled()) {
			LOGGER.debug("Out:{}-{}-{}:{}",prototype,fieldName,type,value);
		}
		AbstractConverterEditor<?> editor=getConverter(prototype,type,fieldName);
		if(editor==null) {
			return value;
		}
		editor.setValue(value);
		return editor.converter();
	}

}
